| JSTL support is fully integrated. You no longer need to use this approach. |
WW2/WX1 and its taglib is oriented towards OGNL, which is using a value stack
for all action properties. These values are not direct available for the
expression language of JSP2/JSTL1.1.
However, it's easy to populate the request
attribute set, with all gettable properties of an action object. You need to provide
an interceptor that does the job, by register a PreResultListener which is
invoked after the return of Action.execute() but before the rendering of the result .
The interceptor below is using Jakarta BeanUtils. It first extracts all getters
of the current action, invokes them one at the time and stores the values into a map.
Then it iterates over the map and populates the request attribute set.
The double iteration is not needed, it's just there for clarity.
class ActionPropertyExportInterceptor
package com.whatever.interceptors;
import com.opensymphony.webwork.WebWorkStatics;
import com.opensymphony.xwork.Action;
import com.opensymphony.xwork.ActionInvocation;
import com.opensymphony.xwork.interceptor.AroundInterceptor;
import com.opensymphony.xwork.interceptor.PreResultListener;
import org.apache.commons.beanutils.PropertyUtils;
import javax.servlet.http.HttpServletRequest;
import java.beans.PropertyDescriptor;
import java.util.*;
/**
* Populates HTTP Request Attributes with all gettable properties of the current action.
*/
public class ActionPropertyExportInterceptor extends AroundInterceptor {
protected void before(ActionInvocation invocation) throws Exception {
invocation.addPreResultListener( new PropertyExporter() );
}
protected void after(ActionInvocation dispatcher, String result) throws Exception { }
public static class PropertyExporter implements PreResultListener {
private static final List ignore = Arrays.asList(new String[] {"class", "texts"});
public void beforeResult(ActionInvocation invocation, String resultCode) {
Map props = extractGetterPropertyValues( invocation.getAction() );
HttpServletRequest request = getRequest(invocation);
for (Iterator it = props.entrySet().iterator(); it.hasNext();) {
Map.Entry e = (Map.Entry) it.next();
request.setAttribute((String) e.getKey(), e.getValue());
}
}
public Map extractGetterPropertyValues(Object bean) {
PropertyDescriptor[] descr = PropertyUtils.getPropertyDescriptors(bean);
Map props = new HashMap();
for (int i = 0; i < descr.length; i++) {
PropertyDescriptor d = descr[i];
if (d.getReadMethod() == null) continue;
if (ignore.contains(d.getName())) continue;
try {
props.put(d.getName(), PropertyUtils.getProperty(bean, d.getName()));
} catch (Exception e) { }
}
return props;
}
public HttpServletRequest getRequest(ActionInvocation invocation) {
return (HttpServletRequest) invocation.getInvocationContext().get(WebWorkStatics.HTTP_REQUEST);
}
}
}
Don't forget to declare the interceptor in your xwork.xml file and insert it
into your interceptor stack.
xwork.xml snippet
<interceptor name="export" class="com.whatever.interceptors.ActionPropertyExportInterceptor" />
. . .
<interceptor-stack name="standard-interceptors">
<interceptor-ref name="timer" />
<interceptor-ref name="logger" />
<interceptor-ref name="params" />
* <interceptor-ref name="export"/>*
<interceptor-ref name="validateParams"/>
<interceptor-ref name="awarePlugger" />
</interceptor-stack>
Your action need to provide getters for all properties that should be exported into the
request attribute set.
class ViewUser
public class ViewUser extends ActionSupport {
private int id;
private User user;
public String execute() throws Exception {
user = findUser( getId() );
return Action.SUCCESS;
}
public int getId() {return id;}
public void setId(int id) {this.id = id;}
* public User getUser() {return user;}*
private User findUser(int id) {...}
}
The User class might look like this
class User
import java.util.Date;
public class User {
private int id;
private String firstName, lastName, email;
private String street, zip, city;
private Date date;
public String getFirstName() {return firstName;}
}
Finally, using the samples above you can write your JSP2 page like this.
ViewUser.jsp
<%@ taglib prefix="c" uri="http: %>
<%@ taglib prefix="fmt" uri="http: %>
<%@ taglib prefix="fn" uri="http: %>
<html>
<head>
<title>Info about ${user.firstName}</title>
</head>
<body>
<h1>Info about ${user.firstName} ${user.lastName} [OS:ID=${user.id}]</h1>
<table border="1" cellspacing="0" cellpadding="2" width="90%" >
<tr>
<th>Name</th> <td>${user.firstName} ${user.lastName}</td>
</tr>
<tr>
<th>Created</th> <td><fmt:formatDate value="${user.date}" pattern="yyyy-MM-dd HH:mm"/></td>
</tr>
<tr>
<th>Email</th> <td>${user.email}</td>
</tr>
<tr>
<th>Address</th> <td>${user.street} ${user.zip} ${fn:toUpperCase(user.city)}</td>
</tr>
</table>
</body>
</html>
Displaying validation errors with JSTL
<c:if test="${!empty fieldErrors || !empty actionErrors}">
<div class="red">
<ul>
<c:forEach items="${fieldErrors}" var="fieldError">
<c:forEach items="${fieldError.value}" var="error">
<li>${error}</li>
</c:forEach>
</c:forEach>
<c:forEach items="${actionErrors}" var="actionError">
<li>${actionError}</li>
</c:forEach>
</ul>
</div>
</c:if>
|